package org.linitly.boot.base.service;

import com.alibaba.fastjson.JSON;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.linitly.boot.base.config.MiniAppConfig;
import org.linitly.boot.base.constant.miniapp.MiniAppConstant;
import org.linitly.boot.base.entity.miniapp.*;
import org.linitly.boot.base.enums.MiniAppResultEnum;
import org.linitly.boot.base.enums.ResultEnum;
import org.linitly.boot.base.exception.CommonException;
import org.linitly.boot.base.utils.JsonUtils;
import org.linitly.boot.base.utils.MiniAppUtil;
import org.linitly.boot.base.utils.RedisOperator;
import org.linitly.boot.base.utils.algorithm.EncryptionUtil;
import org.linitly.boot.base.utils.http.HttpClientUtil;
import org.linitly.boot.base.utils.http.common.HttpConfig;
import org.linitly.boot.base.utils.http.exception.HttpProcessException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.retry.annotation.Retryable;
import org.springframework.stereotype.Service;

/**
 * @author: linitly
 * @date: 2021/5/21 9:29
 * @descrption:
 */
@Slf4j
@Service
public class MiniAppService {

    @Autowired
    private MiniAppConfig miniAppConfig;
    @Autowired
    private RedisOperator redisOperator;

    /**
     * 小程序用户登陆
     */
    public MiniAppLoginResEntity miniAppLogin(String code) {
        String url = miniAppConfig.getCodeToSessionUrl().replace("JSCODE", code)
                .replace("APPID", miniAppConfig.getAppId())
                .replace("SECRET", miniAppConfig.getAppSecret());
        String result = null;
        try {
            result = HttpClientUtil.get(HttpConfig.custom().url(url));
        } catch (HttpProcessException e) {
            e.printStackTrace();
            throw new CommonException("小程序登陆获取openId出错");
        }
        if (StringUtils.isBlank(result)) throw new CommonException("小程序登陆出错，获取内容为空");
        MiniAppLoginResEntity loginResEntity = JsonUtils.humpResponseTransform(result, MiniAppLoginResEntity.class);
        if (loginResEntity != null && StringUtils.isNotBlank(loginResEntity.getOpenid())) {
            return loginResEntity;
        }
        dealResponseCode(loginResEntity.getErrcode(), code, miniAppConfig.getAppId(), miniAppConfig.getAppSecret(), result);
        return loginResEntity;
    }

    /**
     * 获取小程序access_token
     */
    public MiniAppAccessTokenResEntity getAccessToken() {
        String url = miniAppConfig.getAccessTokenUrl().replace("APPID", miniAppConfig.getAppId())
                .replace("SECRET", miniAppConfig.getAppSecret());
        String result = null;
        try {
            result = HttpClientUtil.get(HttpConfig.custom().url(url));
        } catch (HttpProcessException e) {
            e.printStackTrace();
            throw new CommonException("小程序获取access_token出错");
        }
        if (StringUtils.isBlank(result)) throw new CommonException("小程序获取access_token出错，获取内容为空");
        MiniAppAccessTokenResEntity resEntity = JsonUtils.humpResponseTransform(result, MiniAppAccessTokenResEntity.class);
        dealResponseCode(resEntity.getErrcode(), null, miniAppConfig.getAppId(), miniAppConfig.getAppSecret(), result);
        setRedisAccessToken(resEntity.getAccessToken(), resEntity.getExpiresIn());
        return resEntity;
    }

    /**
     * 获取小程序用户信息
     */
    public MiniAppUserInfoEntity getUserInfo(String sessionKey, MiniAppUserInfoReqEntity reqEntity) {
        String signature = EncryptionUtil.sha1(reqEntity.getRawData() + sessionKey);
        if (!reqEntity.getSignature().equals(signature)) throw new CommonException(ResultEnum.VERIFICATION_ERROR);
        MiniAppUserInfoEntity userInfoEntity = JSON.parseObject(reqEntity.getRawData(), MiniAppUserInfoEntity.class);
        String phoneInfo = MiniAppUtil.getPhoneInfo(reqEntity.getEncryptedData(), sessionKey, reqEntity.getIv());
        MiniAppPhoneEntity phoneEntity = JSON.parseObject(phoneInfo, MiniAppPhoneEntity.class);
        userInfoEntity.setPhoneEntity(phoneEntity);
        return userInfoEntity;
    }

    /**
     * 将小程序access_token存储到redis
     */
    private void setRedisAccessToken(String accessToken, long expireTime) {
        long expire = expireTime > MiniAppConstant.ACCESS_TOKEN_EXPIRE_TIME ? MiniAppConstant.ACCESS_TOKEN_EXPIRE_TIME : expireTime > 3600 ? expireTime - 3600 : expireTime;
        redisOperator.set(MiniAppConstant.ACCESS_TOKEN_REDIS_KEY, accessToken, expire);
    }

    /**
     * 获取小程序access_token
     */
    @Retryable(CommonException.class)
    public String getRedisAccessToken() {
        String accessToken = redisOperator.get(MiniAppConstant.ACCESS_TOKEN_REDIS_KEY);
        if (StringUtils.isBlank(accessToken)) {
            MiniAppAccessTokenResEntity tokenResEntity = getAccessToken();
            accessToken = tokenResEntity.getAccessToken();
        }
        return accessToken;
    }

    /**
     * 处理小程序返回码
     */
    private void dealResponseCode(Integer responseCode, String code, String appId, String appSecret, String response) {
        MiniAppResultEnum miniAppResultEnum = MiniAppResultEnum.getResultEnum(responseCode);
        switch (miniAppResultEnum) {
            case SUCCESS:
                return;
            case APP_ID_ERROR:
            case APP_SECRET_ERROR:
            case GRANT_TYPE_ERROR:
            case CODE_INVALID:
            case FREQUENCY_LIMIT:
            case SYSTEM_ERROR:
            case OTHER:
            default:
                miniAppError(MiniAppResultEnum.OTHER, code, appId, appSecret, response);
                break;
        }
    }

    /**
     * 小程序出错处理
     */
    private void miniAppError(MiniAppResultEnum resultEnum, String code, String appId, String appSecret, String response) {
        log.error("小程序API调用出错，请求code为：{}，appid为：{}，secret为：{}，返回信息为：{}", code, appId, appSecret, response);
        throw new CommonException(resultEnum.getCode(), resultEnum.getMessage());
    }
}

